package com.yfp.machine.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.yfp.common.core.constant.CacheConstants;
import com.yfp.common.core.constant.DbConstants;
import com.yfp.common.core.constant.EmqxConstants;
import com.yfp.common.core.context.SecurityContextHolder;
import com.yfp.common.core.domain.R;
import com.yfp.common.core.exception.ServiceException;
import com.yfp.common.core.utils.DeviceNumberUtil;
import com.yfp.common.core.utils.StringUtils;
import com.yfp.common.core.utils.bean.BeanUtils;
import com.yfp.common.datascope.annotation.DataScope;
import com.yfp.common.redis.service.RedisService;
import com.yfp.common.security.utils.SecurityUtils;
import com.yfp.emqx.MqttSendClient;
import com.yfp.machine.domain.*;
import com.yfp.machine.domain.dto.*;
import com.yfp.machine.domain.*;
import com.yfp.machine.domain.vo.MachineVo;
import com.yfp.machine.mapper.MachineMapper;
import com.yfp.machine.mapper.SysUserMapper;
import com.yfp.machine.mapper.TagMachineMapper;
import com.yfp.machine.service.*;
import com.yfp.machine.domain.dto.*;
import com.yfp.machine.service.*;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.data.geo.Point;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * (Machine)表服务实现类
 *
 *
 * @since 2023-12-11 17:38:58
 */
@Slf4j
@Service("machineService")
public class MachineServiceImpl implements MachineService {

    @Resource
    private MachineRoadService machineRoadService;

    @Resource
    private SysUserMapper sysUserMapper;

    @Resource
    private OrderListService orderListService;

    @Resource
    private MachineMapper machineMapper;

    @Resource
    private SysUserService sysUserService;

    @Resource
    private CompanyService companyService;

    @Resource
    private RedisService redisService;

    @Resource
    private MachineIsTagService machineIsTagService;

    @Resource
    private RouteService routeService;

    @Resource
    private PositionService positionService;

    @Resource
    private PositionTypeService positionTypeService;

    @Resource
    private MqttSendClient mqttSendClient;

    @Resource
    private AreaService areaService;

    @Resource
    private TagMachineMapper tagMachineMapper;
    @Resource
    private MealReplacementService mealReplacementService;

    /**
     * 项目启动时，初始化参数到缓存
     */
    @PostConstruct
    public void init() {
        resetConfigCache();
    }

    @Override
    public R<Object> newAdditionMachine(MachineVo machineVo) {
        Long deptId = SecurityUtils.getLoginUser().getSysUser().getDeptId();

        Machine machine = new Machine();
        BeanUtils.copyBeanProp(machine, machineVo);
        machine.setCreateOper(SecurityUtils.getUserId());

        Integer number = this.machineMapper.queryTodayMachineNumber();
        String machineId = DeviceNumberUtil.generateDeviceNumber(machineVo.getMachineType(), Integer.valueOf(number + 1));

        Position position = positionService.queryById(machineVo.getPositionId());
        // 设备编号
        machine.setMachineId(machineId);
        machine.setMachineSn(machineId);
        machine.setDeptId(deptId);
        machine.setCityName(position.getCityName());
        if (this.machineMapper.insert(machine) == 0) return R.fail("新增设备失败");
        clearConfigCache();
        return R.ok("新增成功");
    }


    @Override
    public Machine disabledByMachineId(String id) {
        Machine machine = this.machineMapper.queryById(id);
        if (Objects.equals(machine.getEnable(), DbConstants.machineCorrelation.DISABLED_ABNORMAL)) {
            machine.setEnable(DbConstants.machineCorrelation.ENABLE_NORMAL);
        } else {
            machine.setEnable(DbConstants.machineCorrelation.DISABLED_ABNORMAL);
        }

        Machine machine1 = this.update(machine);
        clearConfigCache();
        return machine1;
    }

    @Override
    public List<Machine> pagingQueryData(Machine machine) {
        Long userId = SecurityUtils.getLoginUser().getUserid();

        if (ObjectUtil.isNull(userId)) throw new ServiceException("未获取到对应用户信息");
        String companyId = SecurityContextHolder.getCompanyId();
        if (StringUtils.isEmpty(companyId)) throw new ServiceException("未获取到对应公司信息");

        List<Machine> machineList = this.machineMapper.selectAll(machine);

        if (CollUtil.isEmpty(machineList)) return Collections.emptyList();
        return machineList;
    }

    public void setMachineDtorelatedData(MachineDto machineDto) {
        Company company = companyService.queryById(machineDto.getCompanyId());

        // 公司
        if (ObjectUtil.isNotNull(company)) {
            machineDto.setCompanyName(company.getName());
        }

        // 路线
        Route route = routeService.queryById(machineDto.getRouteId());
        if (ObjectUtil.isNotNull(route)) {
            machineDto.setRouteName(route.getName());
        }

        // 区域
        Area area = areaService.queryById(machineDto.getAreaId());
        if (ObjectUtil.isNotNull(area)) {
            machineDto.setAreaName(area.getName());
        }

        // 点位
        Position position = positionService.queryById(machineDto.getPositionId());
        if (ObjectUtil.isNotNull(position)) {
            machineDto.setPositionName(position.getPositonName());
            PositionType positionType = positionTypeService.queryById(position.getPositionTypeId());
            if (ObjectUtil.isNotNull(positionType)) {
                machineDto.setPositionTypeName(positionType.getTypeName());
                // 营业时间
                machineDto.setSaleBeginTime(positionType.getSaleBeginTime());
                machineDto.setSaleEndTime(positionType.getSaleEndTime());
            }
        }


        // 维修
        SysUser sysUser = sysUserService.queryById(Long.valueOf(machineDto.getServiceContracts()));
        if (ObjectUtil.isNotNull(sysUser)) {
            machineDto.setServiceContracts(sysUser.getUserId().toString());
            machineDto.setServiceName(sysUser.getNickName());
            machineDto.setServicePhone(sysUser.getPhonenumber());

        }

        // 标签
        MachineIsTag machineIsTag = machineIsTagService.queryById(machineDto.getMachineId());
        if (ObjectUtil.isNotNull(machineIsTag)) {
            machineDto.setMachineTagId(machineIsTag.getTagId());
        }

        // 创建人
        SysUser createOper = sysUserService.queryById(Long.valueOf(machineDto.getCreateOper()));
        if (ObjectUtil.isNotNull(createOper)) {
            machineDto.setCreateOper(createOper.getNickName());
        }

        // 套餐数及设备套餐上限
        MachineComboDetailsDto comboDetails = machineRoadService.getComboDetails(machineDto.getMachineId());
        if (ObjectUtil.isNotNull(comboDetails)) {

            machineDto.setComboTotal(comboDetails.getTotal());
            comboDetails.setTotal(Objects.isNull(comboDetails.getTotal()) ? "0" : comboDetails.getTotal());
            machineDto.setThreshold(Objects.isNull(machineDto.getThreshold()) ? "0" : machineDto.getThreshold());
            machineDto.setInventoryAlarm(Integer.parseInt(comboDetails.getTotal()) < Integer.parseInt(machineDto.getThreshold()));

        }
        // 销售额
        String totalAmt = orderListService.queryMachineSalesAmount(machineDto.getMachineId());
        if (CharSequenceUtil.isNotEmpty(totalAmt)) {
            machineDto.setSalesAmount(totalAmt);
        }
        if (machineDto.getOnlineStatus().equals(DbConstants.machineCorrelation.MACHINE_STATUS_OFFLINE)) {
            long between = DateUtil.between(machineDto.getOfflineUpdateTime(), new Date(), DateUnit.MINUTE);
            machineDto.setOfflineDuration(between + "分钟");
        }

        // 客服在线状态
        Boolean operationalOnlineStatus = mealReplacementService.getOperationalOnlineStatus(machineDto.getMachineId());
        machineDto.setOperationalOnlineStatus(BooleanUtil.toInt(operationalOnlineStatus));
    }

    @Override
    public List<PositionMachine> findEnterpriseMachine(Integer machineType) {
        List<PositionMachine> positionMachines = this.machineMapper.findEnterpriseMachine(machineType);
        if (CollUtil.isEmpty(positionMachines)) return Collections.emptyList();
        return positionMachines.stream().map(positionMachine -> {
            String mId = positionMachine.getMachineId();
            TagMachine tagMachine = this.tagMachineMapper.queryByMachineId(mId);
            if (ObjectUtil.isNotNull(tagMachine)) {
                positionMachine.setMachineTag(tagMachine.getTagName());
            }
            return positionMachine;
        }).toList();
    }

    /**
     * 播放地址
     * @param serialNumber
     * @return
     */
    @Override
    public R<String> playback(String serialNumber) {

        String playUrl = "";
        if (redisService.hasKey(serialNumber)) {
            Object cacheObject = redisService.getCacheObject(serialNumber);
            playUrl = getUrl(serialNumber, cacheObject.toString());
            System.out.println(cacheObject);
        } else {
            String body = HttpUtil.createPost("https://open.ys7.com/api/lapp/token/get?appKey=87ac73f689a240eca565c1df8cdbbb43&appSecret=19fbec9affb88afcf307f19ce12cc3ce")
                    .execute()
                    .body();
            JsonElement jsonElement = JsonParser.parseString(body);
            JsonObject jsonObject = jsonElement.getAsJsonObject();
            String code = jsonObject.get("code").getAsString();
            if (code.equals("200")) {
                String accessToken = jsonObject.get("data").getAsJsonObject().get("accessToken").getAsString();

                playUrl = getUrl(serialNumber, accessToken);

                // 存6天  单位分钟
                redisService.setCacheObject(serialNumber, accessToken, 8640L, TimeUnit.MINUTES);

            } else {
                log.info("获取萤石token失败，入参:{}",serialNumber);
                return R.fail(jsonObject.get("msg").getAsString());
            }
        }

        return R.ok(playUrl);
    }


    /**
     * 获取播放地址
     * @param serialNumber
     * @param accessToken
     * @return
     */
    public String getUrl(String serialNumber, String accessToken) {
        String url = "";
        // 萤石获取播放地址的路径
        String a = "https://open.ys7.com/api/lapp/v2/live/address/get?";
        String body1 = HttpUtil.createPost(a+"accessToken=" + accessToken + "&deviceSerial=" + serialNumber)
                .execute()
                .body();

        JsonElement jsonElement1 = JsonParser.parseString(body1);
        JsonObject jsonObject1 = jsonElement1.getAsJsonObject();
        String code1 = jsonObject1.get("code").getAsString();
        if (code1.equals("200")) {
            url = jsonObject1.get("data").getAsJsonObject().get("url").getAsString();
        } else {
            log.info("获取播放地址失败,摄像头序列号:{},,token:{},,msg:{}",serialNumber,accessToken,jsonObject1.get("msg"));
        }

        String playUrl = "https://open.ys7.com/ezopen/h5/iframe?url=" + url + "&autoplay=1&accessToken=" + accessToken;

        return playUrl;
    }


    @Override
    public R<Object> updateByMachine(Machine machine) {
        if (CharSequenceUtil.isNotEmpty(machine.getMachineTagId())) {
            MachineIsTag machineIsTag = new MachineIsTag();
            machineIsTag.setMachineId(machine.getMachineId());
            machineIsTag.setTagId(machine.getMachineTagId());
            machineIsTagService.insertOrUpdate(machineIsTag);
        }
        Position position = positionService.queryById(machine.getPositionId());
        machine.setCityName(position.getCityName());

        boolean result = machineMapper.update(machine) > 0;
        clearConfigCache();
        return result ? R.ok() : R.fail("修改失败");
    }

    @Override
    public MachineDto queryMachineInfoById(String machineId) {
        Machine machine = this.machineMapper.queryById(machineId);
        if (ObjectUtil.isNull(machine)) return null;

        MachineDto machineDto = new MachineDto();
        BeanUtil.copyProperties(machine, machineDto);
        setMachineDtorelatedData(machineDto);
        return machineDto;
    }


    /**
     * 修改数据
     *
     * @param machine 实例对象
     * @return 实例对象
     */
    @Override
    public Machine update(Machine machine) {
        this.machineMapper.update(machine);
        return this.machineMapper.queryById(machine.getMachineId());
    }


    @Override
    public void loadingConfigCache() {
        List<Machine> machines = this.machineMapper.selectNormalMachines();
        if (CollUtil.isEmpty(machines)) return;
        redisCacheLongitudeAndLatitude(machines);
    }

    @Override
    public void clearConfigCache() {
        log.info("清除RedisGEO坐标经纬度缓存");
        redisService.deleteObject(CacheConstants.GEO_KEY);
    }

    @Override
    public void resetConfigCache() {
        clearConfigCache();
        loadingConfigCache();
    }

    @Override
    public boolean restartByMachineId(String id) {

        String recordNo = UUID.randomUUID().toString();
        JSONObject pushObj = new JSONObject();
        pushObj.set(EmqxConstants.TOPIC_PUSH_MSG_TYPE, EmqxConstants.TOPIC_PUSH_RESTART);
        pushObj.set(EmqxConstants.TOPIC_PUSH_TIME, DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
        pushObj.set(EmqxConstants.TOPIC_PUSH_RECORD_NO, recordNo);
        try {
            mqttSendClient.publish(false, id, pushObj.toString());
        } catch (MqttException e) {
            log.info("推送设备重启失败");
            return false;
        }
        return true;
    }

    /**
     * 查询在线/离线设备
     *
     * @param machine 机器
     * @return {@link Object}
     */
    @Override
    public Object queryOnlineMachine(Machine machine) {
        List<MachineDto> machineDtos = this.pagingQueryData(machine).stream().map(machines -> {

            MachineDto machineDto = new MachineDto();
            BeanUtil.copyProperties(machines, machineDto);
            setMachineDtorelatedData(machineDto);
            return machineDto;

        }).toList();
        if (machineDtos.isEmpty()) return Collections.emptyMap();

        long offlineSize = machineDtos.stream().filter(machineDto -> ObjectUtil.isNull(machineDto.getOnlineStatus()) || machineDto.getOnlineStatus().equals(DbConstants.machineCorrelation.MACHINE_STATUS_OFFLINE)).count();

        long onlineSize = machineDtos.size() - offlineSize;

        Map<String, String> machineOnlineInfo = new HashMap<>();
        machineOnlineInfo.put("machineSize", String.valueOf(machineDtos.size()));
        machineOnlineInfo.put("online", String.valueOf(onlineSize));
        machineOnlineInfo.put("offline", String.valueOf(offlineSize));

        return machineOnlineInfo;
    }

    /**
     * 通过点位ID或设备类型标签获取设备列表
     *
     * @param positionTypeId 点位类型id
     * @return {@link List}<{@link PositionMachine}>
     */
    @Override
    public List<PositionMachine> machineListByPositionIdOrMachineTag(String positionTypeId, String machineTag, String machineName, String machineId, Integer machineType,String cityName) {
        PositionMachineDTO dto = new PositionMachineDTO();
        dto.setMachineTag(machineTag);
        dto.setMachineName(machineName);
        dto.setMachineType(machineType);
        dto.setPositionTypeId(positionTypeId);
        dto.setMachineId(machineId);
        dto.setCityName(cityName);
        List<PositionMachine> positionMachines = this.machineMapper.machineListByPositionIdOrMachineTag(dto);
        if (CollUtil.isEmpty(positionMachines)) return Collections.emptyList();

        return positionMachines.stream().map(positionMachine -> {
            String mId = positionMachine.getMachineId();
            TagMachine tagMachine = this.tagMachineMapper.queryByMachineId(mId);
            if (ObjectUtil.isNotNull(tagMachine)) {
                positionMachine.setMachineTag(tagMachine.getTagName());
            }
            return positionMachine;
        }).toList();
    }

    /**
     * 设备分析
     *
     * @param positionId 点位
     * @param startTime  开始时间
     * @param endTime    结束时间
     * @param cityName
     * @return {@link MachineAnalysisDto}
     */
    @Override
    public List<MachineAnalysisDto> machineAnalysis(String positionId, Date startTime, Date endTime, String cityName) {
        List<MachineAnalysisDto> machineAnalysisDtoList = getMachineAnalysisDtos(positionId, cityName);

        if (CollectionUtil.isEmpty(machineAnalysisDtoList)) return Collections.emptyList();

        for (MachineAnalysisDto machineAnalysisDto : machineAnalysisDtoList) {

            getMachineAnalysisInfo(startTime, endTime, machineAnalysisDto);

        }
        return machineAnalysisDtoList;
    }

    @Override
    public Machine getMachineById(String machineId) {
        return this.machineMapper.queryById(machineId);
    }

    @Override
    public List<String> getMachineNamesByMachineIds(List<String> machineIds) {
        return this.machineMapper.getMachineNamesByMachineIds(machineIds);
    }

    @Override
    @DataScope(deptAlias = "m")
    public List<String> queryMachineIdsByMachineName(String machineName) {
        return this.machineMapper.queryMachineIdsByMachineName(machineName);
    }


    /**
     * 获取设备数据
     *
     * @param positionId 位置id
     * @param cityName
     * @return {@link List}<{@link MachineAnalysisDto}>
     */
    private List<MachineAnalysisDto> getMachineAnalysisDtos(String positionId, String cityName) {
        List<MachineAnalysisDto> machineAnalysisDtoList;
        Machine machine = new Machine();
        machine.setPositionId(positionId);
        machine.setCityName(cityName);
        machineAnalysisDtoList = machineMapper.queryMachineInfoAndPositionInfo(machine);
        return machineAnalysisDtoList;
    }

    /**
     * 获取设备分析信息
     *
     * @param startTime          开始时间
     * @param endTime            结束时间
     * @param machineAnalysisDto 机器分析dto
     */
    private void getMachineAnalysisInfo(Date startTime, Date endTime, MachineAnalysisDto machineAnalysisDto) {
        // 设备销售金额
        String salesAmount = queryMachineSalesAmount(machineAnalysisDto.getMachineId(), startTime, endTime);
        if (CharSequenceUtil.isNotEmpty(salesAmount)) machineAnalysisDto.setSalesAmount(salesAmount);

        // 设备实际销量
        String actualSalesVolume = queryMachineActualSalesVolume(machineAnalysisDto.getMachineId(), startTime, endTime);
        if (CharSequenceUtil.isNotEmpty(actualSalesVolume)) machineAnalysisDto.setActualSalesVolume(actualSalesVolume);

        // 上周同日环比
        String lastWeekOnSameDayBasis = queryLastWeekOnSameDayBasis(machineAnalysisDto.getMachineId(), startTime, endTime);
        if (CharSequenceUtil.isNotEmpty(lastWeekOnSameDayBasis))
            machineAnalysisDto.setLastWeekOnSameDayBasis(lastWeekOnSameDayBasis);

        // 上周同日平均环比
        String lastWeekAverageBasis = queryLastWeekAverageBasis(machineAnalysisDto.getMachineId(), startTime, endTime);
        if (CharSequenceUtil.isNotEmpty(lastWeekAverageBasis))
            machineAnalysisDto.setLastWeekAverageBasis(lastWeekAverageBasis);

        // 投放数量
        String inputComboNum = queryMachineInputComboNum(machineAnalysisDto.getMachineId(), startTime, endTime);
        if (CharSequenceUtil.isNotEmpty(inputComboNum)) machineAnalysisDto.setInputComboNum(inputComboNum);

        // 餐品剩余
        String comboSurplusNum = queryComboSurplusNum(machineAnalysisDto.getMachineId());
        if (CharSequenceUtil.isNotEmpty(comboSurplusNum)) machineAnalysisDto.setComboSurplusNum(comboSurplusNum);

        // 报废率
        if (CharSequenceUtil.isNotEmpty(inputComboNum) && CharSequenceUtil.isNotEmpty(comboSurplusNum)) {
            String scrapRate = NumberUtil.roundStr(NumberUtil.div(comboSurplusNum, inputComboNum).toString(), 2);
            machineAnalysisDto.setScrapRate(scrapRate);
        }
    }

    private String queryComboSurplusNum(String machineId) {
        return this.machineMapper.queryComboSurplusNum(machineId);
    }

    /**
     * 上周平均环比
     *
     * @param machineId 机器id
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return {@link String}
     */
    private String queryLastWeekAverageBasis(String machineId, Date startTime, Date endTime) {
        // 本周
        String actualSalesVolume = queryMachineActualSalesVolume(machineId, startTime, endTime);

        long betweenDay = DateUtil.betweenDay(startTime, endTime, true);

        DateTime lastWeekStartTime;
        if (betweenDay == 1) {
            lastWeekStartTime = DateUtil.offsetDay(startTime, -7);
            startTime = DateUtil.offsetDay(endTime, -7);
        } else {
            lastWeekStartTime = DateUtil.offsetDay(startTime, -(int) betweenDay);
        }

        // 上周
        String lastWeekActualSalesVolume = queryMachineActualSalesVolume(machineId, lastWeekStartTime, startTime);

        if (CharSequenceUtil.isEmpty(lastWeekActualSalesVolume) && CharSequenceUtil.isEmpty(actualSalesVolume)) {
            return "0";
        } else if (CharSequenceUtil.isEmpty(lastWeekActualSalesVolume) && CharSequenceUtil.isNotEmpty(actualSalesVolume)) {
            String aveLastWeek = "1";
            if (actualSalesVolume.equals("0") || CharSequenceUtil.isEmpty(actualSalesVolume)) actualSalesVolume = "1";
            if (betweenDay == 0) betweenDay = 1;
            String aveCurrent = NumberUtil.div(actualSalesVolume, String.valueOf(betweenDay)).toString();

            String result = NumberUtil.div(String.valueOf(NumberUtil.sub(aveCurrent, "0")), aveLastWeek).toString();
            return NumberUtil.roundStr(result, 2);
        } else if (CharSequenceUtil.isEmpty(actualSalesVolume) && CharSequenceUtil.isNotEmpty(lastWeekActualSalesVolume)) {
            String aveLastWeek = NumberUtil.div(lastWeekActualSalesVolume, String.valueOf(betweenDay)).toString();
            String aveCurrent = "0";

            String result = NumberUtil.div(String.valueOf(NumberUtil.sub(aveCurrent, aveLastWeek)), aveLastWeek).toString();
            return NumberUtil.roundStr(result, 2);
        } else {
            String aveLastWeek = NumberUtil.div(lastWeekActualSalesVolume, String.valueOf(betweenDay)).toString();
            String aveCurrent = NumberUtil.div(actualSalesVolume, String.valueOf(betweenDay)).toString();

            String result = NumberUtil.div(String.valueOf(NumberUtil.sub(aveCurrent, aveLastWeek)), aveLastWeek).toString();
            return NumberUtil.roundStr(result, 2);
        }
    }

    /**
     * 上周环比
     *
     * @param machineId 机器id
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return {@link String}
     */
    private String queryLastWeekOnSameDayBasis(String machineId, Date startTime, Date endTime) {
        String actualSalesVolume = queryMachineActualSalesVolume(machineId, startTime, endTime);

        long betweenDay = DateUtil.betweenDay(startTime, endTime, true);
        DateTime lastWeekStartTime;
        if (betweenDay == 1) {
            lastWeekStartTime = DateUtil.offsetDay(startTime, -7);
            startTime = DateUtil.offsetDay(endTime, -7);
        } else {
            lastWeekStartTime = DateUtil.offsetDay(startTime, -(int) betweenDay);
        }

        String lastWeekActualSalesVolume = queryMachineActualSalesVolume(machineId, lastWeekStartTime, startTime);

        if (CharSequenceUtil.isEmpty(lastWeekActualSalesVolume) && CharSequenceUtil.isEmpty(actualSalesVolume)) {
            return "0";
        } else if (CharSequenceUtil.isEmpty(lastWeekActualSalesVolume) && CharSequenceUtil.isNotEmpty(actualSalesVolume)) {

            lastWeekActualSalesVolume = "1";
            String result = NumberUtil.div(String.valueOf(NumberUtil.sub(actualSalesVolume, "0")), lastWeekActualSalesVolume).toString();
            return NumberUtil.roundStr(result, 2);

        } else if (CharSequenceUtil.isEmpty(actualSalesVolume) && CharSequenceUtil.isNotEmpty(lastWeekActualSalesVolume)) {

            actualSalesVolume = "0";
            String result = NumberUtil.div(String.valueOf(NumberUtil.sub(actualSalesVolume, lastWeekActualSalesVolume)), lastWeekActualSalesVolume).toString();
            return NumberUtil.roundStr(result, 2);

        } else {
            //（本周数据-上周数据）/上周数据 * 100%
            String result = NumberUtil.div(String.valueOf(NumberUtil.sub(actualSalesVolume, lastWeekActualSalesVolume)), lastWeekActualSalesVolume).toString();

            return NumberUtil.roundStr(result, 2);
        }
    }

    /**
     * 查询机器输入组合号
     *
     * @param machineId 机器id
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return {@link String}
     */
    private String queryMachineInputComboNum(String machineId, Date startTime, Date endTime) {
        return this.machineMapper.queryMachineInputComboNum(machineId, startTime, endTime);
    }

    /**
     * 查询机器实际销售量
     *
     * @param machineId 机器id
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return {@link String}
     */
    private String queryMachineActualSalesVolume(String machineId, Date startTime, Date endTime) {
        return this.machineMapper.queryMachineActualSalesVolume(machineId, startTime, endTime);

    }

    /**
     * 查询机器销售金额
     *
     * @param machineId 机器id
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return {@link String}
     */
    private String queryMachineSalesAmount(String machineId, Date startTime, Date endTime) {
        return this.machineMapper.queryMachineSalesAmount(machineId, startTime, endTime);
    }


    /**
     * Redis缓存经度和纬度
     *
     * @param machineList 机器列表
     */
    private void redisCacheLongitudeAndLatitude(List<Machine> machineList) {
        clearConfigCache();
        String geoKey = CacheConstants.GEO_MACHINE_KEY;
        Map<String, Point> points = new HashMap<>();
        machineList.forEach(machine -> {
            if (StringUtils.isNotEmpty(machine.getLatitude()) && StringUtils.isNotEmpty(machine.getLongitude())) {
                String lat = machine.getLatitude();
                String lng = machine.getLongitude();
                points.put(machine.getMachineId(), new Point(Double.parseDouble(lng), Double.parseDouble(lat)));
            }
        });
        redisService.setGeoCache(geoKey, points);
        log.info("加载RedisGEO坐标经纬度缓存成功");

    }
}

